/*
 * @Description: 地图测距
 * @Author: wangqian
 * @LastEditors: wangqian
 * @Date: 2020-12-18 16:12:24
 * @LastEditTime: 2021-01-18 21:34:01
 */

//当鼠标移除地图视图的时为帮助提示要素添加隐藏样式
// $(map.getViewport()).on("mouseout", function() {
//   $(this.helpTooltipElement).addClass("hidden");
// });

export default class Mapping {
  constructor(props) {
    //这里初始化class的时候需要传map对象进来
    this.map = props;
    //定义矢量数据源
    this.source = null; 
    //定义矢量图层
    this.vector = new ol.layer.Vector({
      style: new ol.style.Style({
        fill: new ol.style.Fill({
          color: "rgba(255,255,255,0.2)"
        }),
        stroke: new ol.style.Stroke({
          color: "#e21e0a",
          width: 2
        }),
        image: new ol.style.Circle({
          radius: 5,
          fill: new ol.style.Fill({
            color: "#ffcc33"
          })
        })
      })
    });
    this.clickEvent;
    this.dbclickEvent;
    //创建一个WGS84球体对象
    this.wgs84Sphere = new ol.Sphere(6378137);
    //创建一个当前要绘制的对象
    this.sketch = new ol.Feature();
    //创建一个帮助提示框对象
    this.helpTooltipElement;
    //创建一个帮助提示信息对象
    this.helpTooltip;
    //创建一个测量提示框对象(动)
    this.measureTooltipElement;
    //创建一个测量提示信息对象（动）
    this.measureTooltip;
    //创建一个测量提示框对象(静)
    this.measureTooltipStaticElement;
    //创建一个测量提示信息对象(静)
    this.measureTooltipStatic = {};
    //继续绘制多边形的提示信息
    this.continuePolygonMsg = "单击以继续绘制多边形";
    //继续绘制线段的提示信息
    this.continueLineMsg = "单击以继续绘制直线";
    //获取大地测量复选框
    this.geodesicCheckbox = true;
    //获取类型
    this.typeSelect;
    //定义一个交互式绘图对象
    this.draw;
    this.measureTooltipText;
    this.listener;//定义一个事件监听
    this.counter = -1;
    this.currentRangingIndex = 0; // 当前测距的索引
    this.init();
  }
  init() {
    this.source = new ol.source.Vector();
    this.vector.setSource(this.source);
    //将矢量图层添加到地图中
    this.map.addLayer(this.vector);
  }
  //鼠标移动触发的函数
  pointerMoveHandler(evt) {
    //Indicates if the map is currently being dragged.
    //Only set for POINTERDRAG and POINTERMOVE events. Default is false.
    //如果是平移地图则直接结束
    if (evt.dragging) {
      return;
    }
    //帮助提示信息
    let helpMsg = "单击开始";

    if (this.sketch) {
      //获取绘图对象的几何要素
      let geom = this.sketch.getGeometry();
      //如果当前绘制的几何要素是多线段，则将绘制提示信息设置为多线段绘制提示信息
      if (geom instanceof ol.geom.Polygon) {
        helpMsg = this.continuePolygonMsg;
      } else if (geom instanceof ol.geom.LineString) {
        helpMsg = this.continueLineMsg;
      }
    }
    //设置帮助提示要素的内标签为帮助提示信息
    this.helpTooltipElement && (this.helpTooltipElement.innerHTML = helpMsg);
    //设置帮助提示信息的位置
    this.helpTooltip && this.helpTooltip.setPosition(evt.coordinate);
    //移除帮助提示要素的隐藏样式
    this.helpTooltipElement && $(this.helpTooltipElement).removeClass("hidden");
  }
  isArray(o){
    return Object.prototype.toString.call(o)== '[object Array]';
  }

  //添加交互式绘图对象的函数
  addInteraction() {
    // this.source = new ol.source.Vector();
    // this.sourceList.push(this.source);
    // this.vector.setSource(this.source);

    console.log('添加交互式绘图对象的函数=',this.counter,this.source)
    this.counter++; 
    // 获取当前选择的绘制类型
    let type = this.typeSelect == "area" ? "Polygon" : "LineString";
    //创建一个交互式绘图对象
    this.draw = new ol.interaction.Draw({
      //绘制的数据源
      source: this.source,
      //绘制类型
      type: type,
      //样式
      style: new ol.style.Style({
        fill: new ol.style.Fill({
          color: "rgba(255,255,255,0.2)"
        }),
        stroke: new ol.style.Stroke({
          color: "rgba(0,0,0,0.5)",
          lineDash: [10, 10],
          width: 2
        }),
        image: new ol.style.Circle({
          radius: 5,
          stroke: new ol.style.Stroke({
            color: "rgba(0,0,0,0.7)"
          }),
          fill: new ol.style.Fill({
            color: "rgba(255,255,255,0.2)"
          })
        })
      })
    });
    //将交互绘图对象添加到地图中
    this.map.addInteraction(this.draw);
    //创建帮助提示框
    this.createHelpTooltip();
    //定义一个控制鼠标点击次数的变量
    let count = 0;

    //地图单击事件
    this.clickEvent = this.map.on("singleclick", (evt)=> {
        // console.log("singleclick=",count,evt.coordinate);
        let text = count == 0 ? '起点' : this.measureTooltipText;
        //如果是第一次点击，则设置测量提示框的文本内容为起点
        this.createPoint(evt.coordinate);//根据鼠标点击位置生成一个点,将该点要素添加到矢量数据源中

        //创建测量提示框 设置测量提示信息的位置坐标，用来确定鼠标点击后测量提示框的位置
        let overlay = this.createMeasureTooltipStatic(text,false,evt.coordinate);
        // overlay.setPosition(evt.coordinate);
        //点击次数增加
        count++;
    });
    this.dbclickEvent = this.map.on("dblclick", (evt)=> {
        console.log('dbclick')
         evt.preventDefault(); 
        // const dblClickInteraction = this.map
        // .getInteractions()
        // .getArray()
        // .find(interaction => {
        //   return interaction instanceof ol.interaction.DoubleClickZoom;
        // });
        // this.map.removeInteraction(dblClickInteraction);
    })

    //绘制开始事件
    this.draw.on("drawstart", (evt)=> {
        console.log('drawstart')
        this.sketch = evt.feature;
        evt.feature.set('class',this.counter);
        //提示框的坐标
        let tooltipCoord = evt.coordinate;
        
        //创建测量提示框
         this.createMeasureTooltip();

        //监听几何要素的change事件
        this.listener = this.sketch.getGeometry().on("change", (evt)=> {
            // console.log('监听几何要素的change事件=',evt)
          //The event target.
          //获取绘制的几何对象
          let geom = evt.target;
          // console.log("Polygon=", geom instanceof ol.geom.Polygon);
          // console.log("LineString=", geom instanceof ol.geom.LineString);
          //定义一个输出对象，用于记录面积和长度
          let output;
          if (geom instanceof ol.geom.Polygon) {
            // map.removeEventListener('singleclick');
            // map.removeEventListener('dblclick');
            //输出多边形的面积
            output = this.formatArea(geom);
            //获取多变形内部点的坐标
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          } else if (geom instanceof ol.geom.LineString) {
            //输出多线段的长度
            output = this.formatLength(geom);
            //获取多线段的最后一个点的坐标
            tooltipCoord = geom.getLastCoordinate();
          }
          if(this.measureTooltipElement){
            //设置测量提示框的内标签为最终输出结果
            this.measureTooltipElement.innerHTML = output;
            this.measureTooltipText = output;
            //设置测量提示信息的位置坐标
            this.measureTooltip.setPosition(tooltipCoord);
          }
        });
    }, this );
    //绘制结束事件
    this.draw.on("drawend",(evt)=> {
        console.log('drawend')
        //设置偏移量
        this.measureTooltip.setOffset([0, -7]);
        //清空绘制要素
        this.sketch = null;
        //清空测量提示要素
        this.measureTooltipElement = null;
        this.map.removeOverlay(this.measureTooltip);

        const coordinates = evt.target.sketchCoords_;
        const coordinate = coordinates[coordinates.length - 1];

        //创建测量提示框
        this.createMeasureTooltipStatic(this.measureTooltipText,true,coordinate);

        //根据鼠标点击位置生成一个点
        this.createPoint(coordinate); 

        count = 0;
        this.removeHelpTooltip();
        this.map.removeInteraction(this.draw);//移除之前的绘制对象
        this.unByKey();
    }, this);
 
    //触发pointermove事件
    this.map.on("pointermove", this.pointerMoveHandler.bind(this));
  }
  //创建帮助提示框
  createHelpTooltip() {
    //如果已经存在帮助提示框则移除
    if (this.helpTooltipElement) {
      this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
    }
    //创建帮助提示要素的div
    this.helpTooltipElement = document.createElement("div");
    //设置帮助提示要素的样式
    this.helpTooltipElement.className = "tooltip hidden";
    //创建一个帮助提示的覆盖标注
    this.helpTooltip = new ol.Overlay({
      element: this.helpTooltipElement,
      offset: [15, 0],
      positioning: "center-left"
    });
    //将帮助提示的覆盖标注添加到地图中
    this.map.addOverlay(this.helpTooltip);
  }

  /**
   * @description: 移除帮助提示框
   * @param {*}
   * @return {*}
   */
  removeHelpTooltip(){
    this.map.removeOverlay(this.helpTooltip);
    this.helpTooltipElement = null;
    this.helpTooltip = null;
  }
  /**
   * @description: 根据鼠标点击位置生成一个点
   * @param {*} coordinate
   * @return {*}
   */
  createPoint(coordinate){
    let point = new ol.geom.Point(coordinate);
    let feature = new ol.Feature({
        geometry:point,
        class:this.counter,
    });
    this.source.addFeature(feature);//将该点要素添加到矢量数据源中
  }
  removeMeasureTooltip(){
       // 删除当前的要素
        let features = this.source.getFeatures();
        features.forEach(e => {
            if(e.get('class') == this.currentRangingIndex){
                this.source.removeFeature(e);
            }
        });

       // 移除当前覆盖物
       let overlays = this.map.getOverlays().array_;
       console.log('overlays',overlays)
       overlays.forEach((o,i) => {
            console.log('index=',i)
           let el = o.getElement();
           let classIndex = el.getAttribute('index');
            console.log('当前覆盖物class=',classIndex,this.currentRangingIndex)
            if(classIndex == this.currentRangingIndex){
                // this.map.removeOverlay(o);
            }
      });
  }
  /**
   * @description: 创建测量提示框（静态）
   * @param {String} text
   * @param {Boolean} end 
   * @param {Array} coordinate 坐标
   */
  createMeasureTooltipStatic(text,end,coordinate) {
    //创建测量提示框的div
    this.measureTooltipStaticElement = document.createElement("div");
    this.measureTooltipStaticElement.setAttribute('index',this.counter);
    //设置测量提示要素的样式
    this.measureTooltipStaticElement.className = "tooltip tooltip-static";
    let innerHtml = '';
    if(end){
        innerHtml = `
            <span>${text}</span>
            <i class="remove el-icon-close" index=${this.counter}></i>
        `
    }else{
        innerHtml = `<span>${text}</span>`
    }

   this.measureTooltipStaticElement.addEventListener('click', (e)=> {
        e = e || window.event;
        if(e.srcElement.className === 'remove el-icon-close'){
            this.currentRangingIndex = e.srcElement.getAttribute('index');
            console.log('当前删除测距的索引=',this.currentRangingIndex);
            this.removeMeasureTooltip();
        }
    })

    this.measureTooltipStaticElement.innerHTML = innerHtml;
    !this.isArray(this.measureTooltipStatic[this.counter]) && (this.measureTooltipStatic[this.counter] = []);
    //创建一个测量提示的覆盖标注
    let overlay = new ol.Overlay({
      element: this.measureTooltipStaticElement,
      offset: [0, -15],
      positioning: "bottom-center",
      className:this.counter,
    }); 
    //将测量提示的覆盖标注添加到地图中
    this.map.addOverlay(overlay);
    overlay.setPosition(coordinate);
    this.measureTooltipStatic[this.counter].push(overlay);
  }
  //创建测量提示框（动态）
  createMeasureTooltip() {
     //创建测量提示框的div
    this.measureTooltipElement = document.createElement("div");
    this.measureTooltipElement.setAttribute("id", "lengthLabel");
    //设置测量提示要素的样式
    this.measureTooltipElement.className = "tooltip tooltip-measure";
    //创建一个测量提示的覆盖标注
    this.measureTooltip = new ol.Overlay({
      element: this.measureTooltipElement,
      offset: [0, -15],
      positioning: "bottom-center"
    });
    //将测量提示的覆盖标注添加到地图中
    this.map.addOverlay(this.measureTooltip);
  }
  //格式化测量长度
  formatLength(line) {
    //定义长度变量
    let length;
    //如果大地测量复选框被勾选，则计算球面距离
    if (this.geodesicCheckbox) {
      //获取坐标串
      let coordinates = line.getCoordinates();
      //初始长度为0
      length = 0;
      //获取源数据的坐标系
      let sourceProj = this.map.getView().getProjection();
      //进行点的坐标转换
      for (let i = 0; i < coordinates.length - 1; i++) {
        //第一个点
        let c1 = ol.proj.transform(coordinates[i], sourceProj, "EPSG:4326");
        //第二个点
        let c2 = ol.proj.transform(coordinates[i + 1], sourceProj, "EPSG:4326");
        //获取转换后的球面距离
        //Returns the distance from c1 to c2 using the haversine formula.
        length += this.wgs84Sphere.haversineDistance(c1, c2);
      }
    } else {
      //计算平面距离
      length = Math.round(line.getLength() * 100) / 100;
    }
    //定义输出变量
    let output;
    //如果长度大于1000，则使用km单位，否则使用m单位
    if (length > 1000) {
      output = Math.round((length / 1000) * 100) / 100 + " " + "km"; //换算成KM单位
    } else {
      output = Math.round(length * 100) / 100 + " " + "m"; //m为单位
    }
    return output;
  }

  //格式化测量面积
  formatArea(polygon) {
    //定义面积变量
    let area;
    //如果大地测量复选框被勾选，则计算球面面积
    if (this.geodesicCheckbox) {
      //获取初始坐标系
      let sourceProj = this.map.getView().getProjection();
      let geom = polygon.clone().transform(sourceProj, "EPSG:4326");
      //获取多边形的坐标系
      let coordinates = geom.getLinearRing(0).getCoordinates();
      //获取球面面积
      area = Math.abs(this.wgs84Sphere.geodesicArea(coordinates));
    } else {
      //获取平面面积
      area = polygon.getArea();
    }
    //定义输出变量
    let output;
    //当面积大于10000时，转换为平方千米，否则为平方米
    if (area > 10000) {
      output = Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
    } else {
      output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
    }
    return output;
  }
  
  /**
   * @description: 移除当前测距
   * @param {*}
   * @return {*}
   */
  removeCurrentRanging(){
    // this.measureTooltipStatic[this.currentRangingIndex].forEach((o)=>{
    //     this.map.removeOverlay(o); 
    // })
  }

  // 删除数据源中的要素  清除当前图层
  clear() {
    // 移除overrlay覆盖物
    // this.measureTooltipStatic[this.currentRangingIndex].forEach((o)=>{
    //     this.map.removeOverlay(o); 
    // })

    this.source.clear();
    this.source = null;
    this.map.removeLayer(this.vector); //从地图中删除给定的图层。
    this.vector = null;
  }

  // 移除事件监听
  unByKey() {
    ol.Observable.unByKey(this.clickEvent);
    ol.Observable.unByKey(this.dbclickEvent);
    ol.Observable.unByKey(this.listener);
    this.clickEvent = null;
    this.dbclickEvent = null;
  }

  // 当页面退出
  destroy() {
    this.clear();
    this.unByKey();
    this.map = null;
  }
}
